package ua.edu.sumdu.j2se.roman.lab1.domain;

import java.io.Serializable;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
 * Author: Radko Roman.
 */

/**
 * The following class a container for some tasks that described in TaskClass.
 * With next methods can perform the following manipulations with tasks: add, remove, size, clear and iterator.
 * In turn, iterator contains following methods: hasNext, next and remove.
 *
 */
public class LinkedTaskList extends TaskList implements Serializable {

    /**
     * This method adds new task to the task list.
     *
     * @param task new task to be added, not null
     * @throws IllegalArgumentException when task is null
     */
    @Override
    public void add(Task task) {
        // in case if adding task is null
        if (task == null) {
            throw new NullPointerException("Task can not be null.");
        }
        Node newNode = new Node(task);
        Node current = head;
        while (current.getNext() != null) {
            current = current.getNext();
        }
        current.setNext(newNode);
    }

    /**
     * This method removes the task from the list.
     * If task is not in list - do nothing.
     *
     * @param task task that you need to remove from the list, not null
     * @throws  IllegalArgumentException when task is null
     */
    @Override
    public void remove(Task task) {
        // in case if deleting task is null
        if (task == null) {
            throw new NullPointerException("Task can not be null.");
        }

        Node current = head;
        while (!current.getNext().getTask().equals(task)) {
            current = current.getNext();
        }
        current.setNext(current.getNext().getNext());
    }

    /**
     * This method return the size of the list.
     *
     * @return size of the list
     */
    @Override
    public int size() {
        Node current = head;
        int counter = 0;
        while (current.getNext() != null) {
            current = current.getNext();
            counter++;
        }
        return counter;
    }

    /**
     * This method implements Iterable interface.
     *
     * @return  Iterator<Task>
     */
    @Override
    public Iterator<Task> iterator() {
        return new Iterator<Task>() {
            private Node current = head;
            private Node previous;

            /**
             * Returns true if the iteration has more elements.
             *
             * @return  the next element in the iteration.
             */
            @Override
            public boolean hasNext() {
                if (current.getNext() == null) {
                    return false;
                }
                return true;
            }

            /**
             * Returns the next element in the iteration.
             *
             * @return the next element in the iteration.
             * @throws NoSuchElementException when the task has not next
             */
            @Override
            public Task next() {
                if (!hasNext()) {
                    throw new NoSuchElementException("Iteration has no more elements");
                }
                previous = current;
                current = current.getNext();
                return current.getTask();
            }

            /**
             * Removes from the underlying collection the last element returned by the iterator.
             * This method can be called only once per call to next.
             * The behavior of an iterator is unspecified if the underlying collection is modified
             * while the iteration is in progress in any way other than by calling this method.
             *
             * @throws IllegalStateException    throws if the next method has not yet been called, or the remove
             * 							        method has already been called after the last call to the next method.
             */
            @Override
            public void remove() {
                if (isLastActionRemove()) {
                    throw new IllegalStateException("Next method has not yet been called, or the remove method has already been called after the last call to the next method");
                }
                previous.setNext(current.getNext());
                current = previous;
            }
            private boolean isLastActionRemove() {
                return current == previous;
            }
        };

    }

    /**
     * This method performs duplicating of object LinkedTaskList type, with means creating a new object identical to current.
     *
     * @return  clone of current list
     * @throws  CloneNotSupportedException  Thrown to indicate that the clone method in class Object has been called to clone an
     *                                      object, but that the object's class does not implement the Cloneable interface.
     */
    @Override
    public TaskList clone() throws CloneNotSupportedException {
        LinkedTaskList result = (LinkedTaskList) super.clone();
        result.head = new Node(null);
        for (Task task: this) {
            result.add(task);
        }
        return result;
    }

    /**
     * This inner class described chain link with following methods: setNext, getTask, getNext.
     *
     * @throws  CloneNotSupportedException  Thrown to indicate that the clone method in class Object has been called to clone an
     *                                      object, but that the object's class does not implement the Cloneable interface.
     */
    class Node implements Cloneable, Serializable {
        public Node clone() throws CloneNotSupportedException {
            Node cloneNode = (Node) super.clone();
            return cloneNode;
        }
        /**
         * This is constructor of class Node.
         *
         * @param element task in link
         */
        public Node(Task element) {
            this.task = element;
        }

        /**
         * This method set next link.
         *
         * @param e link which contains task
         */
        public void setNext(Node e) {
            this.next = e;
        }

        /**
         * This method get task from link
         *
         * @return  task
         */
        public Task getTask() {
            return task;
        }

        /**
         * This method return next link after current.
         *
         * @return  link
         */
        public Node getNext() {
            return next;
        }

        /**
         * This variable means next chain link.
         */
        private Node next = null;

        /**
         * This variable means contents of the link.
         */
        private Task task;
    }

    /**
     * This field means first empty chain link.
     */
    private Node head = new Node(null);

}


